/*-------------------------------------------------------------------------
 *
 * spgtextproc.c
 *	  文本的基数树（压缩 trie）实现
 *
 * 在 text_ops SPGiST 索引中，内元组可以具有一个前缀，该前缀是所有在该元组下索引的字符串的公共前缀。节点标签表示字符串在前缀后的下一个字节。假设我们总是使用最长的前缀，除非前缀长度由 SPGIST_MAX_PREFIX_LENGTH 限制，否则我们将获得多个节点标签。
 *
 * 要重构任何索引条目的索引字符串，请将从根节点开始的内元组前缀和节点标签连接在一起，并沿着树向下直至叶节点条目，然后追加叶节点条目中的数据。 （在向下遍历树时，“level”是到目前为止已重构的字节数。）
 *
 * 但是，节点标签有两个特殊情况：-1 表示在前缀之后没有更多字节，-2 表示我们不得不拆分现有的 allTheSame 元组（在这种情况下，我们必须创建一个不对应于任何字符串字节的节点标签）。在这两种情况下，节点标签都不会对重构的字符串产生任何贡献。
 *
 * 先前，我们为这两个特殊情况使用零作为节点标签，但这存在问题，因为人们无法判断当前级别结束的字符串是否可以降级到这样的子节点中。为了向后兼容，我们仍然支持这样的节点标签用于读取；但是不会有新的条目被推送到零标签子节点中。同样，不会有新的条目被推送到 -2 标签的子节点中。
 *
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * IDENTIFICATION
 *			src/backend/access/spgist/spgtextproc.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "access/spgist.h"
#include "catalog/pg_type.h"
#include "mb/pg_wchar.h"
#include "utils/builtins.h"
#include "utils/datum.h"
#include "utils/pg_locale.h"
#include "utils/varlena.h"


/*
 * 在最坏的情况下，文本基数树中的内部元组可能有多达258个节点（每个可能的字节值一个，加上两个特殊情况）。每个节点在MAXALIGN=8的机器上可以占用16个字节。内部元组必须适合大小为BLCKSZ的索引页。与其假设我们知道页面头、元组头等所施加的确切开销，不如留出100个字节用于此（截至本文撰写时，实际开销不应超过56个字节，因此这个数字有一定的余量）。因此，我们可以安全地创建最大长度为BLCKSZ - 258 * 16 - 100字节的前缀。不幸的是，因为258 * 16超过了4K，当BLCKSZ小于8K时，没有安全的前缀长度；如果在树的某个位置有太多不同的下一个字节值，始终可能会出现“SPGiST内部元组大小超过最大值”。由于在实际应用中使用非标准块大小的情况似乎微不足道，我们现在就只能接受这一事实，当BLCKSZ配置小于默认值时，选择最大前缀大小为32字节。
*/
#define SPGIST_MAX_PREFIX_LENGTH	Max((int) (BLCKSZ - 258 * 16 - 100), 32)

/*
 * 针对文本的排序敏感操作符的策略等同于btree策略加上10的值。
 *
 * 当前排序敏感的策略及其对应的btree策略：
 * 11 BTLessStrategyNumber
 * 12 BTLessEqualStrategyNumber
 * 14 BTGreaterEqualStrategyNumber
 * 15 BTGreaterStrategyNumber
*/
#define SPG_STRATEGY_ADDITION	(10)
#define SPG_IS_COLLATION_AWARE_STRATEGY(s) ((s) > SPG_STRATEGY_ADDITION \
										 && (s) != RTPrefixStrategyNumber)

/* 用于在picksplit中排序值的结构 */
typedef struct spgNodePtr
{
	Datum		d;
	int			i;
	int16		c;
} spgNodePtr;


Datum spg_text_config(PG_FUNCTION_ARGS)
{
	/* spgConfigIn *cfgin = (spgConfigIn *) PG_GETARG_POINTER(0); */
	spgConfigOut *fc_cfg = (spgConfigOut *) PG_GETARG_POINTER(1);

	fc_cfg->prefixType = TEXTOID;
	fc_cfg->labelType = INT2OID;
	fc_cfg->canReturnData = true;
	fc_cfg->longValuesOK = true;	/* 后缀将缩短长值 */
	PG_RETURN_VOID();
}

/*
 * 从给定的不一定以空结尾的字符串形成文本datum，
 * 如果可能，使用短的varlena头格式
*/
static Datum fc_formTextDatum(const char *fc_data, int fc_datalen)
{
	char	   *fc_p;

	fc_p = (char *) palloc(fc_datalen + VARHDRSZ);

	if (fc_datalen + VARHDRSZ_SHORT <= VARATT_SHORT_MAX)
	{
		SET_VARSIZE_SHORT(fc_p, fc_datalen + VARHDRSZ_SHORT);
		if (fc_datalen)
			memcpy(fc_p + VARHDRSZ_SHORT, fc_data, fc_datalen);
	}
	else
	{
		SET_VARSIZE(fc_p, fc_datalen + VARHDRSZ);
		memcpy(fc_p + VARHDRSZ, fc_data, fc_datalen);
	}

	return PointerGetDatum(fc_p);
}

/*
 * 找到a和b的公共前缀的长度
*/
static int fc_commonPrefix(const char *fc_a, const char *fc_b, int fc_lena, int fc_lenb)
{
	int			fc_i = 0;

	while (fc_i < fc_lena && fc_i < fc_lenb && *fc_a == *fc_b)
	{
		fc_a++;
		fc_b++;
		fc_i++;
	}

	return fc_i;
}

/*
 * 在int16 datum数组中进行二分搜索以寻找与c匹配的项
 *
 * 成功时，*i得到匹配位置；失败时，它得到插入位置
*/
static bool fc_searchChar(Datum *fc_nodeLabels, int fc_nNodes, int16 fc_c, int *fc_i)
{
	int			fc_StopLow = 0,
				fc_StopHigh = fc_nNodes;

	while (fc_StopLow < fc_StopHigh)
	{
		int			fc_StopMiddle = (fc_StopLow + fc_StopHigh) >> 1;
		int16		fc_middle = DatumGetInt16(fc_nodeLabels[fc_StopMiddle]);

		if (fc_c < fc_middle)
			fc_StopHigh = fc_StopMiddle;
		else if (fc_c > fc_middle)
			fc_StopLow = fc_StopMiddle + 1;
		else
		{
			*fc_i = fc_StopMiddle;
			return true;
		}
	}

	*fc_i = fc_StopHigh;
	return false;
}

Datum spg_text_choose(PG_FUNCTION_ARGS)
{
	spgChooseIn *fc_in = (spgChooseIn *) PG_GETARG_POINTER(0);
	spgChooseOut *fc_out = (spgChooseOut *) PG_GETARG_POINTER(1);
	text	   *fc_inText = DatumGetTextPP(fc_in->datum);
	char	   *fc_inStr = VARDATA_ANY(fc_inText);
	int			fc_inSize = VARSIZE_ANY_EXHDR(fc_inText);
	char	   *fc_prefixStr = NULL;
	int			fc_prefixSize = 0;
	int			fc_commonLen = 0;
	int16		fc_nodeChar = 0;
	int			fc_i = 0;

	/* 检查前缀匹配，将nodeChar设置为前缀后的第一个字节 */
	if (fc_in->hasPrefix)
	{
		text	   *fc_prefixText = DatumGetTextPP(fc_in->prefixDatum);

		fc_prefixStr = VARDATA_ANY(fc_prefixText);
		fc_prefixSize = VARSIZE_ANY_EXHDR(fc_prefixText);

		fc_commonLen = fc_commonPrefix(fc_inStr + fc_in->level,
								 fc_prefixStr,
								 fc_inSize - fc_in->level,
								 fc_prefixSize);

		if (fc_commonLen == fc_prefixSize)
		{
			if (fc_inSize - fc_in->level > fc_commonLen)
				fc_nodeChar = *(unsigned char *) (fc_inStr + fc_in->level + fc_commonLen);
			else
				fc_nodeChar = -1;
		}
		else
		{
			/* 必须拆分元组，因为输入值与前缀不匹配 */
			fc_out->resultType = spgSplitTuple;

			if (fc_commonLen == 0)
			{
				fc_out->result.splitTuple.prefixHasPrefix = false;
			}
			else
			{
				fc_out->result.splitTuple.prefixHasPrefix = true;
				fc_out->result.splitTuple.prefixPrefixDatum =
					fc_formTextDatum(fc_prefixStr, fc_commonLen);
			}
			fc_out->result.splitTuple.prefixNNodes = 1;
			fc_out->result.splitTuple.prefixNodeLabels =
				(Datum *) palloc(sizeof(Datum));
			fc_out->result.splitTuple.prefixNodeLabels[0] =
				Int16GetDatum(*(unsigned char *) (fc_prefixStr + fc_commonLen));

			fc_out->result.splitTuple.childNodeN = 0;

			if (fc_prefixSize - fc_commonLen == 1)
			{
				fc_out->result.splitTuple.postfixHasPrefix = false;
			}
			else
			{
				fc_out->result.splitTuple.postfixHasPrefix = true;
				fc_out->result.splitTuple.postfixPrefixDatum =
					fc_formTextDatum(fc_prefixStr + fc_commonLen + 1,
								  fc_prefixSize - fc_commonLen - 1);
			}

			PG_RETURN_VOID();
		}
	}
	else if (fc_inSize > fc_in->level)
	{
		fc_nodeChar = *(unsigned char *) (fc_inStr + fc_in->level);
	}
	else
	{
		fc_nodeChar = -1;
	}

	/* 在节点标签数组中查找nodeChar */
	if (fc_searchChar(fc_in->nodeLabels, fc_in->nNodes, fc_nodeChar, &fc_i))
	{
		/*
		 * 下降到现有节点。（如果in->allTheSame，核心代码将
		 * 忽略我们在这里的nodeN规范，但这没关系。我们仍然必须
		 * 提供正确的levelAdd和restDatum值，而且这些值与
		 * 由核心选择的节点无关。）
*/
		int			fc_levelAdd;

		fc_out->resultType = spgMatchNode;
		fc_out->result.matchNode.nodeN = fc_i;
		fc_levelAdd = fc_commonLen;
		if (fc_nodeChar >= 0)
			fc_levelAdd++;
		fc_out->result.matchNode.levelAdd = fc_levelAdd;
		if (fc_inSize - fc_in->level - fc_levelAdd > 0)
			fc_out->result.matchNode.restDatum =
				fc_formTextDatum(fc_inStr + fc_in->level + fc_levelAdd,
							  fc_inSize - fc_in->level - fc_levelAdd);
		else
			fc_out->result.matchNode.restDatum =
				fc_formTextDatum(NULL, 0);
	}
	else if (fc_in->allTheSame)
	{
		/*
		 * 无法使用AddNode操作，因此拆分元组。上层元组与
		 * 之前相同的前缀，并为下层元组使用一个虚拟节点标签-2。
		 * 下层元组没有前缀，并且与原始元组具有相同的节点
		 * 标签。
		 *
		 * 注意：如果上层元组有前缀，似乎很诱人缩短其前缀，
		 * 然后使用其最后一个字节作为下层元组的标签。
		 * 但这没有优势，因为我们知道输入值与
		 * 整个前缀匹配：我们最终只会再次拆分下层元组。
*/
		fc_out->resultType = spgSplitTuple;
		fc_out->result.splitTuple.prefixHasPrefix = fc_in->hasPrefix;
		fc_out->result.splitTuple.prefixPrefixDatum = fc_in->prefixDatum;
		fc_out->result.splitTuple.prefixNNodes = 1;
		fc_out->result.splitTuple.prefixNodeLabels = (Datum *) palloc(sizeof(Datum));
		fc_out->result.splitTuple.prefixNodeLabels[0] = Int16GetDatum(-2);
		fc_out->result.splitTuple.childNodeN = 0;
		fc_out->result.splitTuple.postfixHasPrefix = false;
	}
	else
	{
		/* 为未见过的 nodeChar 值添加节点 */
		fc_out->resultType = spgAddNode;
		fc_out->result.addNode.nodeLabel = Int16GetDatum(fc_nodeChar);
		fc_out->result.addNode.nodeN = fc_i;
	}

	PG_RETURN_VOID();
}

/* qsort 比较器，用于按 "c" 对 spgNodePtr 结构排序 */
static int fc_cmpNodePtr(const void *fc_a, const void *fc_b)
{
	const spgNodePtr *fc_aa = (const spgNodePtr *) fc_a;
	const spgNodePtr *fc_bb = (const spgNodePtr *) fc_b;

	return fc_aa->c - fc_bb->c;
}

Datum spg_text_picksplit(PG_FUNCTION_ARGS)
{
	spgPickSplitIn *fc_in = (spgPickSplitIn *) PG_GETARG_POINTER(0);
	spgPickSplitOut *fc_out = (spgPickSplitOut *) PG_GETARG_POINTER(1);
	text	   *fc_text0 = DatumGetTextPP(fc_in->datums[0]);
	int			fc_i,
				fc_commonLen;
	spgNodePtr *fc_nodes;

	/* 识别最长公共前缀（如果有的话） */
	fc_commonLen = VARSIZE_ANY_EXHDR(fc_text0);
	for (fc_i = 1; fc_i < fc_in->nTuples && fc_commonLen > 0; fc_i++)
	{
		text	   *fc_texti = DatumGetTextPP(fc_in->datums[fc_i]);
		int			fc_tmp = fc_commonPrefix(VARDATA_ANY(fc_text0),
									   VARDATA_ANY(fc_texti),
									   VARSIZE_ANY_EXHDR(fc_text0),
									   VARSIZE_ANY_EXHDR(fc_texti));

		if (fc_tmp < fc_commonLen)
			fc_commonLen = fc_tmp;
	}

	/*
	 * 如果有必要，限制前缀长度，以确保生成的
	 * 内部元组可以放在一个页面上。
	 */
	fc_commonLen = Min(fc_commonLen, SPGIST_MAX_PREFIX_LENGTH);

	/* 将节点前缀设置为该字符串（如果它不为空） */
	if (fc_commonLen == 0)
	{
		fc_out->hasPrefix = false;
	}
	else
	{
		fc_out->hasPrefix = true;
		fc_out->prefixDatum = fc_formTextDatum(VARDATA_ANY(fc_text0), fc_commonLen);
	}

	/* 从每个值中提取节点标签（第一个非公共字节） */
	fc_nodes = (spgNodePtr *) palloc(sizeof(spgNodePtr) * fc_in->nTuples);

	for (fc_i = 0; fc_i < fc_in->nTuples; fc_i++)
	{
		text	   *fc_texti = DatumGetTextPP(fc_in->datums[fc_i]);

		if (fc_commonLen < VARSIZE_ANY_EXHDR(fc_texti))
			fc_nodes[fc_i].c = *(unsigned char *) (VARDATA_ANY(fc_texti) + fc_commonLen);
		else
			fc_nodes[fc_i].c = -1;	/* 如果字符串全部是公共的则使用 -1 */
		fc_nodes[fc_i].i = fc_i;
		fc_nodes[fc_i].d = fc_in->datums[fc_i];
	}

	/*
	 * 根据标签值排序，以便我们可以将值分组到节点中。 这
	 * 也确保节点按标签值排序，从而允许在 searchChar 中
	 * 使用二进制搜索。
	 */
	qsort(fc_nodes, fc_in->nTuples, sizeof(*fc_nodes), fc_cmpNodePtr);

	/* 并发出结果 */
	fc_out->nNodes = 0;
	fc_out->nodeLabels = (Datum *) palloc(sizeof(Datum) * fc_in->nTuples);
	fc_out->mapTuplesToNodes = (int *) palloc(sizeof(int) * fc_in->nTuples);
	fc_out->leafTupleDatums = (Datum *) palloc(sizeof(Datum) * fc_in->nTuples);

	for (fc_i = 0; fc_i < fc_in->nTuples; fc_i++)
	{
		text	   *fc_texti = DatumGetTextPP(fc_nodes[fc_i].d);
		Datum		fc_leafD;

		if (fc_i == 0 || fc_nodes[fc_i].c != fc_nodes[fc_i - 1].c)
		{
			fc_out->nodeLabels[fc_out->nNodes] = Int16GetDatum(fc_nodes[fc_i].c);
			fc_out->nNodes++;
		}

		if (fc_commonLen < VARSIZE_ANY_EXHDR(fc_texti))
			fc_leafD = fc_formTextDatum(VARDATA_ANY(fc_texti) + fc_commonLen + 1,
								  VARSIZE_ANY_EXHDR(fc_texti) - fc_commonLen - 1);
		else
			fc_leafD = fc_formTextDatum(NULL, 0);

		fc_out->leafTupleDatums[fc_nodes[fc_i].i] = fc_leafD;
		fc_out->mapTuplesToNodes[fc_nodes[fc_i].i] = fc_out->nNodes - 1;
	}

	PG_RETURN_VOID();
}

Datum spg_text_inner_consistent(PG_FUNCTION_ARGS)
{
	spgInnerConsistentIn *fc_in = (spgInnerConsistentIn *) PG_GETARG_POINTER(0);
	spgInnerConsistentOut *fc_out = (spgInnerConsistentOut *) PG_GETARG_POINTER(1);
	bool		fc_collate_is_c = lc_collate_is_c(PG_GET_COLLATION());
	text	   *fc_reconstructedValue;
	text	   *fc_reconstrText;
	int			fc_maxReconstrLen;
	text	   *fc_prefixText = NULL;
	int			fc_prefixSize = 0;
	int			fc_i;

	/*
	 * 重建此元组表示的值，包括父数据、
	 * 如果有的话此元组的前缀，以及如果不是虚的
	 * 节点标签。 in->level 应该是之前重建值的长度，
	 * 此处添加的字节数是 prefixSize 或 prefixSize + 1。
	 *
	 * 注意：我们假设 in->reconstructedValue 不是压缩的，并且没有
	 * 短的 varlena 头。 这是可以的，因为它必须是
	 * 由之前调用此例程创建的，并且我们总是发出
	 * 长格式的重建值。
	 */
	fc_reconstructedValue = (text *) DatumGetPointer(fc_in->reconstructedValue);
	Assert(fc_reconstructedValue == NULL ? fc_in->level == 0 :
		   VARSIZE_ANY_EXHDR(fc_reconstructedValue) == fc_in->level);

	fc_maxReconstrLen = fc_in->level + 1;
	if (fc_in->hasPrefix)
	{
		fc_prefixText = DatumGetTextPP(fc_in->prefixDatum);
		fc_prefixSize = VARSIZE_ANY_EXHDR(fc_prefixText);
		fc_maxReconstrLen += fc_prefixSize;
	}

	fc_reconstrText = palloc(VARHDRSZ + fc_maxReconstrLen);
	SET_VARSIZE(fc_reconstrText, VARHDRSZ + fc_maxReconstrLen);

	if (fc_in->level)
		memcpy(VARDATA(fc_reconstrText),
			   VARDATA(fc_reconstructedValue),
			   fc_in->level);
	if (fc_prefixSize)
		memcpy(((char *) VARDATA(fc_reconstrText)) + fc_in->level,
			   VARDATA_ANY(fc_prefixText),
			   fc_prefixSize);
	/* reconstrText 的最后一个字节将在下面填充 */

	/*
	 * 扫描子节点。 对于每一个，完成重建值
	 * 并检查它是否与查询一致。 如果一致，则发出条目到
	 * 输出数组。
	 */
	fc_out->nodeNumbers = (int *) palloc(sizeof(int) * fc_in->nNodes);
	fc_out->levelAdds = (int *) palloc(sizeof(int) * fc_in->nNodes);
	fc_out->reconstructedValues = (Datum *) palloc(sizeof(Datum) * fc_in->nNodes);
	fc_out->nNodes = 0;

	for (fc_i = 0; fc_i < fc_in->nNodes; fc_i++)
	{
		int16		fc_nodeChar = DatumGetInt16(fc_in->nodeLabels[fc_i]);
		int			fc_thisLen;
		bool		fc_res = true;
		int			fc_j;

		/* 如果 nodeChar 是虚拟值， 则不要将其包含在数据中 */
		if (fc_nodeChar <= 0)
			fc_thisLen = fc_maxReconstrLen - 1;
		else
		{
			((unsigned char *) VARDATA(fc_reconstrText))[fc_maxReconstrLen - 1] = fc_nodeChar;
			fc_thisLen = fc_maxReconstrLen;
		}

		for (fc_j = 0; fc_j < fc_in->nkeys; fc_j++)
		{
			StrategyNumber fc_strategy = fc_in->scankeys[fc_j].sk_strategy;
			text	   *fc_inText;
			int			fc_inSize;
			int			fc_r;

			/*
			 * 如果它是一个感知排序的操作符，但排序是 C，我们
			 * 可以将其视为非感知排序。 对于非 C 排序，我们
			 * 需要遍历整个树 :-( 所以这里没有意义去做
			 * 任何检查。 （还要注意，我们的重建值可能会
			 * 以部分多字节字符结束，因此对它应用
			 * 任何编码敏感的测试都是冒险的。）
			 */
			if (SPG_IS_COLLATION_AWARE_STRATEGY(fc_strategy))
			{
				if (fc_collate_is_c)
					fc_strategy -= SPG_STRATEGY_ADDITION;
				else
					continue;
			}

			fc_inText = DatumGetTextPP(fc_in->scankeys[fc_j].sk_argument);
			fc_inSize = VARSIZE_ANY_EXHDR(fc_inText);

			fc_r = memcmp(VARDATA(fc_reconstrText), VARDATA_ANY(fc_inText),
					   Min(fc_inSize, fc_thisLen));

			switch (fc_strategy)
			{
				case BTLessStrategyNumber:
				case BTLessEqualStrategyNumber:
					if (fc_r > 0)
						fc_res = false;
					break;
				case BTEqualStrategyNumber:
					if (fc_r != 0 || fc_inSize < fc_thisLen)
						fc_res = false;
					break;
				case BTGreaterEqualStrategyNumber:
				case BTGreaterStrategyNumber:
					if (fc_r < 0)
						fc_res = false;
					break;
				case RTPrefixStrategyNumber:
					if (fc_r != 0)
						fc_res = false;
					break;
				default:
					elog(ERROR, "unrecognized strategy number: %d",
						 fc_in->scankeys[fc_j].sk_strategy);
					break;
			}

			if (!fc_res)
				break;			/* 不需要考虑其余条件 */
		}

		if (fc_res)
		{
			fc_out->nodeNumbers[fc_out->nNodes] = fc_i;
			fc_out->levelAdds[fc_out->nNodes] = fc_thisLen - fc_in->level;
			SET_VARSIZE(fc_reconstrText, VARHDRSZ + fc_thisLen);
			fc_out->reconstructedValues[fc_out->nNodes] =
				datumCopy(PointerGetDatum(fc_reconstrText), false, -1);
			fc_out->nNodes++;
		}
	}

	PG_RETURN_VOID();
}

Datum spg_text_leaf_consistent(PG_FUNCTION_ARGS)
{
	spgLeafConsistentIn *fc_in = (spgLeafConsistentIn *) PG_GETARG_POINTER(0);
	spgLeafConsistentOut *fc_out = (spgLeafConsistentOut *) PG_GETARG_POINTER(1);
	int			fc_level = fc_in->level;
	text	   *fc_leafValue,
			   *fc_reconstrValue = NULL;
	char	   *fc_fullValue;
	int			fc_fullLen;
	bool		fc_res;
	int			fc_j;

	/* 所有测试都是精确的 */
	fc_out->recheck = false;

	fc_leafValue = DatumGetTextPP(fc_in->leafDatum);

	/* 如上所述，in->reconstructedValue 不是压缩的或短的。 */
	if (DatumGetPointer(fc_in->reconstructedValue))
		fc_reconstrValue = (text *) DatumGetPointer(fc_in->reconstructedValue);

	Assert(fc_reconstrValue == NULL ? fc_level == 0 :
		   VARSIZE_ANY_EXHDR(fc_reconstrValue) == fc_level);

	/* 重建此叶子元组表示的完整字符串 */
	fc_fullLen = fc_level + VARSIZE_ANY_EXHDR(fc_leafValue);
	if (VARSIZE_ANY_EXHDR(fc_leafValue) == 0 && fc_level > 0)
	{
		fc_fullValue = VARDATA(fc_reconstrValue);
		fc_out->leafValue = PointerGetDatum(fc_reconstrValue);
	}
	else
	{
		text	   *fc_fullText = palloc(VARHDRSZ + fc_fullLen);

		SET_VARSIZE(fc_fullText, VARHDRSZ + fc_fullLen);
		fc_fullValue = VARDATA(fc_fullText);
		if (fc_level)
			memcpy(fc_fullValue, VARDATA(fc_reconstrValue), fc_level);
		if (VARSIZE_ANY_EXHDR(fc_leafValue) > 0)
			memcpy(fc_fullValue + fc_level, VARDATA_ANY(fc_leafValue),
				   VARSIZE_ANY_EXHDR(fc_leafValue));
		fc_out->leafValue = PointerGetDatum(fc_fullText);
	}

	/* 执行所需的比较 */
	fc_res = true;
	for (fc_j = 0; fc_j < fc_in->nkeys; fc_j++)
	{
		StrategyNumber fc_strategy = fc_in->scankeys[fc_j].sk_strategy;
		text	   *fc_query = DatumGetTextPP(fc_in->scankeys[fc_j].sk_argument);
		int			fc_queryLen = VARSIZE_ANY_EXHDR(fc_query);
		int			fc_r;

		if (fc_strategy == RTPrefixStrategyNumber)
		{
			/*
			 * 如果 level >= 查询长度，则 reconstrValue 必须以
			 * 查询（前缀）字符串开头，因此我们不需要再次检查它。
			 */
			fc_res = (fc_level >= fc_queryLen) ||
				DatumGetBool(DirectFunctionCall2Coll(text_starts_with,
													 PG_GET_COLLATION(),
													 fc_out->leafValue,
													 PointerGetDatum(fc_query)));

			if (!fc_res)			/* 不需要考虑其余条件 */
				break;

			continue;
		}

		if (SPG_IS_COLLATION_AWARE_STRATEGY(fc_strategy))
		{
			/* 感知排序的比较 */
			fc_strategy -= SPG_STRATEGY_ADDITION;

			/* 如果启用断言，验证重建字符串的编码 */
			Assert(pg_verifymbstr(fc_fullValue, fc_fullLen, false));

			fc_r = varstr_cmp(fc_fullValue, fc_fullLen,
						   VARDATA_ANY(fc_query), fc_queryLen,
						   PG_GET_COLLATION());
		}
		else
		{
			/* 非感知排序的比较 */
			fc_r = memcmp(fc_fullValue, VARDATA_ANY(fc_query), Min(fc_queryLen, fc_fullLen));

			if (fc_r == 0)
			{
				if (fc_queryLen > fc_fullLen)
					fc_r = -1;
				else if (fc_queryLen < fc_fullLen)
					fc_r = 1;
			}
		}

		switch (fc_strategy)
		{
			case BTLessStrategyNumber:
				fc_res = (fc_r < 0);
				break;
			case BTLessEqualStrategyNumber:
				fc_res = (fc_r <= 0);
				break;
			case BTEqualStrategyNumber:
				fc_res = (fc_r == 0);
				break;
			case BTGreaterEqualStrategyNumber:
				fc_res = (fc_r >= 0);
				break;
			case BTGreaterStrategyNumber:
				fc_res = (fc_r > 0);
				break;
			default:
				elog(ERROR, "unrecognized strategy number: %d",
					 fc_in->scankeys[fc_j].sk_strategy);
				fc_res = false;
				break;
		}

		if (!fc_res)
			break;				/* 不需要考虑其余条件 */
	}

	PG_RETURN_BOOL(fc_res);
}
